有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaFX中的javaunicode补充平面

我在处理JavaFX中来自补充(“astral”)平面的Unicode字符时遇到问题。具体地说,我不能在TextInputDialog中粘贴这样的字符(取而代之的是一些奇怪的字符,例如ð),也不能在WebView中使用它们(它们被呈现为������

如果我通过JOptionPane.showInputDialog输入相同的字符并将它们打印到控制台,那么这些字符工作得非常好。它们甚至显示在JavaFXAlert中,尽管它在末尾附加了一些垃圾

有没有办法解决这些问题

我正在Linux中使用Oracle JDK 1.8.051版
补充平面字符示例:😀 𐂃 🂡 🙭 𫞂
如果看不到,可能需要安装其他字体,如Symbola或Noto

下面是一个示例程序(使用Label而不是WebView):

import javax.swing.JOptionPane;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.control.TextInputDialog;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class UniTest extends Application {
    @Override
    public void start(final Stage stage) throws Exception {
        final String s = new String(new int[]{127137, 178050, 3232, 128512, 241}, 0, 5);
        System.out.println("The string: " + s);
        System.out.println("Characters: " + s.length());
        System.out.println("Code points: " + s.codePoints().count());

        JOptionPane.showMessageDialog(null, s, "JOptionPane", JOptionPane.INFORMATION_MESSAGE);

        final Alert al = new Alert(AlertType.INFORMATION);
        al.setTitle("Alert");
        al.setContentText(s);
        al.showAndWait();

        final TextInputDialog dlg = new TextInputDialog();
        dlg.setTitle("TextInputDialog");
        dlg.setContentText("Try to paste the string in here");
        dlg.showAndWait().ifPresent(x -> System.out.println("Your input: " + x));

        final StackPane root = new StackPane();
        root.getChildren().add(new Label(s));
        stage.setScene(new Scene(root, 400, 300));
        stage.setTitle("Stage");
        stage.show();
    }

    public static void main(final String... args) {
        launch(args);
    }
}

以下是我得到的结果:

screenshots

注意:本例中并非所有字符都来自补充平面,其中一个字符仅在控制台中正确呈现


共 (2) 个答案

  1. # 1 楼答案

    TL;DR:显然JavaFX有缺陷

    这是你正在使用的文本

    🂡𫞂ಠ😀ñ
    

    十进制码点表示法:

    127137 178050 3232 128512 241
    

    十六进制表示法:

    0x1F0A1 0x2B782 0xCA0 0x1F600 0xF1
    

    显示错误

    Java在内部使用UTF-16。因此,考虑UTF16表示:

    UTF-16代表:

    D83C DCA1 D86D DF82 0CA0 D83D DE00 00F1
    

    我们可以看到,屏幕上显示的是您期望的五个字符,但随后是三个垃圾字符

    因此,它显然试图显示8个字形,其中只有5个。这几乎可以肯定,因为显示代码的计数是8个字符,因为三个字符以UTF-16编码为代理对,所以每个字符有两个16位的字。换句话说,在存在代理项对的情况下,它使用了错误的字符串长度值

    粘贴文本错误

    UTF-8测试数据的表示:

    F0 9F 82 A1 F0 AB 9E 82 E0 B2 A0 F0 9F 98 80 C3 B1
    

    我们看到的是

    00F0 ð LATIN SMALL LETTER ETH 
    009F  <control> = APC = APPLICATION PROGRAM COMMAND 
    0082  <control> = BPH = BREAK PERMITTED HERE
    00A1 ¡ INVERTED EXCLAMATION MARK 
    00F0 ð LATIN SMALL LETTER ETH 
    

    (这两个控制字符可以在某些字体中包含包含缩写或十六进制代码的字形。这些在您的示例中可见。)

    拉丁1十六进制表示法:

    F0 9F 82 A1 F0
    

    请注意,这五个字节与预期文本的UTF-8表示的前五个字节相同

    结论:粘贴的数据被粘贴为5个UTF-8码点,占17字节,但被解释为5个Latin1码点,占5字节。同样,长度使用了错误的属性

  2. # 2 楼答案

    这个问题已经在Java10中得到了解决。 见Java Bug Report